/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2010 huliqing, huliqing.cn@gmail.com
 *
 * This file is part of QFaces.
 * QFaces is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QFaces is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with QFaces.  If not, see <http://www.gnu.org/licenses/>.
 *
 * 这个文件是QFaces的一部分。
 * 您可以单独使用或分发这个文件，但请不要移除这个头部声明信息.
 * QFaces是一个自由软件，您可以自由分发、修改其中的源代码或者重新发布它，
 * 新的任何修改后的重新发布版必须同样在遵守LGPL3或更后续的版本协议下发布.
 * 关于LGPL协议的细则请参考COPYING、COPYING.LESSER文件，
 * 您可以在QFaces的相关目录中获得LGPL协议的副本，
 * 如果没有找到，请连接到 http://www.gnu.org/licenses/ 查看并获取副本。
 *
 * - Author: Huliqing
 * - Contact: huliqing.cn@gmail.com
 * - License: GNU Lesser General Public License (LGPL)
 * - Blog and source code availability: http://www.huliqing.name/
 */

package name.huliqing.qfaces.component;
 
import java.io.IOException;
import java.util.Map;
import java.util.logging.Logger;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;
import name.huliqing.qfaces.QFaces;

public class AjaxSupport extends UIComponentBase implements Ajax{
	private final static Logger logger = Logger.getLogger(AjaxSupport.class.getName());
	
    // 组件参数
	private MethodExpression listener;	// e.g.  public String listener(String[] args)
	private String ref;			// 引用的客户端组件的id, e.g.  "id1,id2,id3"
	private String _for;		// 存放结果的客户端组件的id, e.g. "id1,id2,id3"
	private String event;		// event, e.g. onclick, onmousedown, onmouseon, on...
	private String onStart;		// js 
	private String onProcess;	// js
	private String onFinish;	// 当组件ajax请求结束时调用的js函数名称。
	private Integer delay;		// 延迟执行,该属性只对event为"onload"时起作用。即是在onload后延迟多少时间（毫秒）后执行ajax调用
	
	// 内部参数
	private boolean valid = true;
	private String eventButton;	// 触发event的客户端组件的id

	public MethodExpression getListener() {
		return listener;
	}

	public void setListener(MethodExpression listener) {
		this.listener = listener;
	}
	
	@Override
    public void restoreState(FacesContext fc, Object state) {
		Object[] obj = (Object[]) state;
		super.restoreState(fc, obj[0]);
		this.listener = (MethodExpression) obj[1];
	}
	
	@Override
    public Object saveState(FacesContext fc) {
		Object[] obj = new Object[2];
		obj[0] = super.saveState(fc);
		obj[1] = listener;
		return obj;
	}
	
	@Override
    public void encodeBegin(FacesContext fc) throws IOException {
		// 装载JS资源
		QFaces.getResLoader2().loadJavaScript(this, "AjaxSupport.js");
		
		// 初始化参数
		this.ref = (String) QFaces.getAttribute(this, "ref", "");
		this._for = (String) QFaces.getAttribute(this, "for", "");
		this.event = (String) QFaces.getAttribute(this, "event", "onclick");
		this.onStart = (String) QFaces.getAttribute(this, "onStart", "");
		this.onProcess = (String) QFaces.getAttribute(this, "onProcess", "");
		this.onFinish = (String) QFaces.getAttribute(this, "onFinish", "");
		this.delay = QFaces.convertToInteger((QFaces.getAttribute(this, "delay", "500")));
		this.eventButton = (String) QFaces.getAttribute(this, "eventButton", "");
		
		// 如果找到eventButton,则确定该JSF button 的客户端ID，如果找不到（可能为一般html控件),则保留原ID
		if (this.eventButton != null && !"".equals(this.eventButton)) {
			UIComponent tempButton = this.findComponent(eventButton);
			if (tempButton != null) {
				this.eventButton = tempButton.getClientId(fc);
			}
		}
		
		// 处理客户端长ID, 将组件所引用的ID（short id），转化为客户端的长ID类型，以便客户端
		// 的Javascript可引用
		if (!"".equals(this.ref)) {
			String[] refIds = null;
			if (this.ref.indexOf(",") != -1) {
				refIds = this.ref.split(",");
			} else {
				refIds = new String[]{this.ref};
			}
			StringBuilder tempRef = new StringBuilder("");
			for (String refId : refIds) {
				UIComponent refCom = this.findComponent(refId.trim());
				if (refCom != null) {
					tempRef.append(refCom.getClientId(fc)).append(",");	// 引用了JSF组件
				} else {
					tempRef.append(refId.trim()).append(","); // 可能引用的是一般的Html组件
				}
			}
			this.ref = tempRef.toString();
		}

		// 同 ref
		if (!"".equals(this._for)) {
			String[] forIds = null;
			if (this._for.indexOf(",") != -1) {
				forIds = this._for.split(",");
			} else {
				forIds = new String[]{this._for};
			}
			StringBuilder tempFor = new StringBuilder("");
			for (String forId : forIds) {
				UIComponent forCom = this.findComponent(forId.trim());
				if (forCom != null) {
					tempFor.append(forCom.getClientId(fc)).append(",");	// for 到JSF组件
				} else {
					tempFor.append(forId.trim()).append(","); // for 到一般的Html组件 
				}
			}
			this._for = tempFor.toString();
		}
		
		// 处理最后多余的","号
		if (this.ref.endsWith(",")) this.ref = this.ref.substring(0, this.ref.length() - 1);
		if (this._for.endsWith(",")) this._for = this._for.substring(0, this._for.length() - 1);
	}
	
	@Override
	public void encodeEnd(FacesContext fc) throws IOException {
		if (!this.valid) return;
		// 初始化
		encodeInit(fc);
	}

	public void ajax(Map<String, String> params, FacesContext fc) {
		logger.fine("ajaxInvokeOnGet...");
        String listenerExp = params.get("listenerExp");
        
        MethodExpression _listener = QFaces.createMethodExpression(listenerExp, 
        		String.class, new Class[]{String[].class});
        Integer refCount = Integer.valueOf(params.get("refCount"));
        if (refCount == null) refCount = 0;
        String[] args = new String[refCount];
        for (int i = 0; i < refCount; i++) {
            String refValue = params.get("refValue" + i);
            args[i] = refValue;
        }
        String result = (String) _listener.invoke(fc.getELContext(), new Object[]{args});
        QFaces.getResponse().responseHTML(fc, result);
        logger.fine("End ajaxInvokeOnGet");
	}
	
    private void encodeInit(FacesContext fc) throws IOException {
    	// clientId, componentClass, listener, ref, _for, event, onFinish
		StringBuilder sb = new StringBuilder("Q.F.UIAjaxSupport.init(")
		.append(" \"" + this.getClientId(fc) + "\"")
		.append(",\"" + this.getClass().getName() + "\"")
		.append(",\"" + (this.listener != null ? this.listener.getExpressionString() : "") + "\"")
		.append(",\"" + this.ref + "\"")
		.append(",\"" + this._for + "\"")
		.append(",\"" + this.event + "\"")
		.append(",\"" + this.onStart + "\"")
		.append(",\"" + this.onProcess + "\"")
		.append(",\"" + this.onFinish + "\"")
		.append(",\"" + this.eventButton + "\"")
		.append(",\"" + this.delay.toString() + "\"")
		.append(");");
		QFaces.getResLoader2().loadJavaScriptInit(this, sb.toString());
	}

	@Override
	public String getFamily() {
		return getClass().getName();
	}

	
}
